////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2012 Kentaro Sekimoto  All rights reserved.
////////////////////////////////////////////////////////////////////////////

#include <tinyhal.h>
#include "..\FM3.h"
#include "..\FM3_I2C\FM3_I2C_Driver.h"
#include "FM3_FRAM.h"

#define DEBUG_FRAM
#define DEBUG_FRAM_WriteX
#define LCD_DEBUG_FRAM_EraseBlock
#define LCD_DEBUG_FRAM_Write

#define I2C_STATIC_BUF
#define I2C_FRAM_CH 1
#define I2C_FRAM_SIZE 0x800
#define I2C_buf_SIZE 0x800
#define I2C_FRAM_ADDR   0x50
#define I2C_FRAM_SPEED  1000
#define I2C_WRITE   0
#define I2C_READ    1
#define I2C_TIMEOUT 10000

#ifdef I2C_STATIC_BUF
UINT8 buf[I2C_buf_SIZE+1];
UINT8 qbuf[I2C_buf_SIZE+1];
#endif

BOOL FRAMWriteSub(UINT32 fram_addr, UINT8 *src, UINT32 len, BOOL fErase)
{
    struct I2C_USER_CONFIGURATION I2C_config;
    struct I2C_HAL_XACTION_UNIT xActionUnits[1];
    struct I2C_HAL_XACTION_UNIT *pxActionUnits[1];
    I2C_HAL_XACTION xAction;
#ifndef I2C_STATIC_BUF
    UINT8 buf[I2C_buf_SIZE+1];
    UINT8 qbuf[I2C_buf_SIZE+1];
#endif
    UINT8 status;
    UINT32 size;

    //lcd_printf("FRAMWrite\r\n");
    I2C_config.ClockRate = I2C_FRAM_SPEED;
    while (len > 0) {
        UINT32 timeout = I2C_TIMEOUT;
        I2C_config.Address = (UINT16)(I2C_FRAM_ADDR + (fram_addr >> 8));
        if (len < I2C_buf_SIZE)
            size = len;
        else
            size = I2C_buf_SIZE;
        buf[0] = (UINT8)fram_addr;
        //lcd_printf("addr=%04X\r\n", fram_addr);
        if (fErase)
            memset((void *)(buf+1), 0, size);
        else
            memcpy((void *)(buf+1), (void *)src, size);
        pxActionUnits[0] = &xActionUnits[0];
        I2C_InitializeTransactionUnit((I2C_HAL_XACTION_UNIT *)&xActionUnits[0], (I2C_WORD *)&buf, (I2C_WORD *)&qbuf, (size_t)(size+1), I2C_WRITE);
        I2C_InitializeTransaction(&xAction, I2C_config, (I2C_HAL_XACTION_UNIT **)&pxActionUnits[0], 1);
        I2C_Enqueue(&xAction);
        while (timeout-- > 0) {
            status = I2C_XAction_GetState(&xAction);
            if (status == I2C_HAL_XACTION::c_Status_Completed)
                break;
        }
        //lcd_printf("I2C state = %d\r\n", status);
        if (status != I2C_HAL_XACTION::c_Status_Completed)
            return FALSE;
        //memory_dump((unsigned long)&qbuf[1], size);
        len -= size;
        fram_addr += size;
        src += size;
    }
    return TRUE;
}

BOOL FRAMReadSub(UINT32 fram_addr, UINT8 *dst, UINT32 len, BOOL fEraseCheck)
{
    struct I2C_USER_CONFIGURATION I2C_config;
    struct I2C_HAL_XACTION_UNIT xActionUnits[2];
    struct I2C_HAL_XACTION_UNIT *pxActionUnits[2];
    I2C_HAL_XACTION xAction;
    UINT8 buf;
#ifndef I2C_STATIC_BUF
    UINT8 qbuf[I2C_buf_SIZE+1];
#endif
    UINT8 status;
    UINT32 size;

    //lcd_printf("FRAMRead\r\n");
    I2C_config.ClockRate = I2C_FRAM_SPEED;
    while (len > 0) {
        UINT32 timeout = I2C_TIMEOUT;
        I2C_config.Address = (UINT16)(I2C_FRAM_ADDR + (fram_addr >> 8));
        if (len < I2C_buf_SIZE)
            size = len;
        else
            size = I2C_buf_SIZE;
        buf = (UINT8)fram_addr;
        //lcd_printf("addr=%04X\r\n", fram_addr);
        pxActionUnits[0] = &xActionUnits[0];
        pxActionUnits[1] = &xActionUnits[1];
        I2C_InitializeTransactionUnit((I2C_HAL_XACTION_UNIT *)&xActionUnits[0], (I2C_WORD *)&buf, (I2C_WORD *)&qbuf, 1, I2C_WRITE);
        if (fEraseCheck)
            // Erase Check
            I2C_InitializeTransactionUnit((I2C_HAL_XACTION_UNIT *)&xActionUnits[1], (I2C_WORD *)0, (I2C_WORD *)&qbuf, size, I2C_READ);
        else
            // Read
            I2C_InitializeTransactionUnit((I2C_HAL_XACTION_UNIT *)&xActionUnits[1], (I2C_WORD *)0, (I2C_WORD *)dst, size, I2C_READ);
        I2C_InitializeTransaction(&xAction, I2C_config, (I2C_HAL_XACTION_UNIT **)&pxActionUnits[0], 2);
        I2C_Enqueue(&xAction);
        while (timeout-- > 0) {
            status = I2C_XAction_GetState(&xAction);
            if (status == I2C_HAL_XACTION::c_Status_Completed)
                break;
        }
        //lcd_printf("I2C state = %d\r\n", status);
        if (status != I2C_HAL_XACTION::c_Status_Completed)
            return FALSE;
        if (fEraseCheck) {
            for (UINT32 i = 0; i < size; i++)
                if (qbuf[i] != 0) {
                    return FALSE;
                }
            //memory_dump((unsigned long)qbuf, size);
        } else
            //memory_dump((unsigned long)dst, size);
        len -= size;
        fram_addr += size;
        dst += size;
    }
    return TRUE;
}

BOOL FRAMWrite(UINT32 fram_addr, UINT8 *src, UINT32 len)
{
    return FRAMWriteSub(fram_addr, src, len, FALSE);
}

BOOL FRAMErase(UINT32 fram_addr, UINT32 len)
{
    return FRAMWriteSub(fram_addr, 0, len, TRUE);
}

BOOL FRAMRead(UINT32 fram_addr, UINT8 *dst, UINT32 len)
{
    return FRAMReadSub(fram_addr, dst, len, FALSE);
}

BOOL FRAMIsErased(UINT32 fram_addr, UINT32 len)
{
    return FRAMReadSub(fram_addr, 0, len, TRUE);
}

BOOL FM3_FRAM_Driver::ChipInitialize(void *context)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();
#if defined(DEBUG_FRAM)
    lcd_printf("ChipInitialize()\r\n");
#endif
    return FM3_I2C_Driver::Initialize(I2C_FRAM_CH);
}

BOOL FM3_FRAM_Driver::ChipUnInitialize(void *context)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();
#if defined(DEBUG_FRAM)
    lcd_printf("ChipUnInitialize()\r\n");
#endif
    return FM3_I2C_Driver::Initialize(I2C_FRAM_CH);;
}

const BlockDeviceInfo* FM3_FRAM_Driver::GetDeviceInfo(void *context)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();
#if defined(DEBUG_FRAM)
    //lcd_printf("GetDeviceInfo(context=%x))\r\n", context);
#endif
    BLOCK_CONFIG *config = (BLOCK_CONFIG *)context;
    return config->BlockDeviceInformation;
}

BOOL FM3_FRAM_Driver::Read(void *context, ByteAddress Address, UINT32 NumBytes, BYTE *pSectorBuff)
{
    NATIVE_PROFILE_HAL_DRIVERS_FLASH();
#if defined(DEBUG_FRAM) || defined(DEBUG_FRAM_Read)
    lcd_printf("Read(addr=%x, num=%x, psec=%x)\r\n", Address, NumBytes, pSectorBuff);
#endif
    BOOL flag = FRAMRead((UINT32)Address, (UINT8 *)pSectorBuff, (UINT32)NumBytes);
    return flag;
}

BOOL FM3_FRAM_Driver::Write(void *context, ByteAddress Address, UINT32 NumBytes, BYTE *pSectorBuff, BOOL ReadModifyWrite)
{
#if defined(DEBUG_FRAM) || defined(DEBUG_FRAM_Write)
    lcd_printf("Write(addr=%x, num=%x, psec=%x)\r\n", Address, NumBytes, pSectorBuff);
#endif
#ifdef LCD_DEBUG_FRAM_Write
    ("Write(%x) %x", Address, NumBytes);
#endif
    BOOL flag = FRAMWrite((UINT32)Address, (UINT8 *)pSectorBuff, (UINT32)NumBytes);
#ifdef LCD_DEBUG_FRAM_Write
    (":%d\r\n", flag);
#endif
    return flag;
}

BOOL FM3_FRAM_Driver::Memset(void *context, ByteAddress Address, UINT8 Data, UINT32 NumBytes)
{
    NATIVE_PROFILE_PAL_FLASH();
#if defined(DEBUG_FRAM) || defined(DEBUG_FRAM_Memset)
    lcd_printf("Memset(addr=%x, num=%x, data=%x)\r\n", Address, NumBytes, Data);
#endif
    return FALSE;
}

BOOL FM3_FRAM_Driver::GetSectorMetadata(void *context, ByteAddress SectorStart, SectorMetadata *pSectorMetadata)
{
    NATIVE_PROFILE_PAL_FLASH();
#if defined(DEBUG_FRAM)
    lcd_printf("GetSectorMetadata(start=%x, psecmetadata=%x)\r\n", SectorStart, pSectorMetadata);
#endif
    return FALSE;
}

BOOL FM3_FRAM_Driver::SetSectorMetadata(void *context, ByteAddress SectorStart, SectorMetadata *pSectorMetadata)
{
    NATIVE_PROFILE_PAL_FLASH();
#if defined(DEBUG_FRAM)
    lcd_printf("SetSectorMetadata(start=%x, psecmetadata=%x)\r\n", SectorStart, pSectorMetadata);
#endif
    return FALSE;
}

BOOL FM3_FRAM_Driver::IsBlockErased(void *context, ByteAddress Address, UINT32 BlockLength)
{
    NATIVE_PROFILE_PAL_FLASH();
#if defined(DEBUG_FRAM)
    lcd_printf("IsBlockErased(addr=%x, len=%x)\r\n", Address, BlockLength);
#endif
    return FRAMIsErased((UINT32)Address, BlockLength);
}

BOOL FM3_FRAM_Driver::EraseBlock(void *context, ByteAddress Address)
{
    NATIVE_PROFILE_PAL_FLASH();
#if defined(DEBUG_FRAM) || defined(DEBUG_FRAM_EraseBlock)
    lcd_printf("EraseBlock(addr=%x)\r\n", Address);
#endif
#ifdef LCD_DEBUG_FRAM_EraseBlock
    //lcd_printf("EraseBlock(%x)\r\n", Address);
#endif
    BOOL flag = FRAMErase((UINT32)Address, I2C_FRAM_SIZE);
    return flag;
}

void FM3_FRAM_Driver::SetPowerState(void *context, UINT32 State)
{
    NATIVE_PROFILE_PAL_FLASH();
#if defined(DEBUG_FRAM)
    lcd_printf("SetPowerState(state=%x)\r\n", State);
#endif
    return;
}

#pragma arm section code = "SectionForFlashOperations"

UINT32 FM3_FRAM_Driver::MaxSectorWrite_uSec(void *context)
{
#if defined(DEBUG_FRAM)
    lcd_printf("MaxSectorWrite_uSec()\r\n");
#endif
    NATIVE_PROFILE_PAL_FLASH();
    MEMORY_MAPPED_NOR_BLOCK_CONFIG *config = (MEMORY_MAPPED_NOR_BLOCK_CONFIG *)context;
    return config->BlockConfig.BlockDeviceInformation->MaxSectorWrite_uSec;
}

UINT32 FM3_FRAM_Driver::MaxBlockErase_uSec(void *context)
{
#if defined(DEBUG_FRAM)
    lcd_printf("MaxBlockErase_uSec()\r\n");
#endif
    NATIVE_PROFILE_PAL_FLASH();
    MEMORY_MAPPED_NOR_BLOCK_CONFIG *config = (MEMORY_MAPPED_NOR_BLOCK_CONFIG *)context;

    return config->BlockConfig.BlockDeviceInformation->MaxBlockErase_uSec;
}

struct IBlockStorageDevice g_FRAM_DeviceTable =
{                          
    &FM3_FRAM_Driver::ChipInitialize,
    &FM3_FRAM_Driver::ChipUnInitialize,
    &FM3_FRAM_Driver::GetDeviceInfo,
    &FM3_FRAM_Driver::Read,
    &FM3_FRAM_Driver::Write,
    &FM3_FRAM_Driver::Memset,
    &FM3_FRAM_Driver::GetSectorMetadata,
    &FM3_FRAM_Driver::SetSectorMetadata,
    &FM3_FRAM_Driver::IsBlockErased,
    &FM3_FRAM_Driver::EraseBlock,
    &FM3_FRAM_Driver::SetPowerState,
    &FM3_FRAM_Driver::MaxSectorWrite_uSec,
    &FM3_FRAM_Driver::MaxBlockErase_uSec,
};
